﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web.Mvc;
using iHouse.Common.Helper;
using iHouse.Infrastructure;
using iHouse.Common.Const;

namespace iHouse.Infrastructure
{
    public class PagingCriteriaBinder : IModelBinder
    {
        private Func<IFilterCollect> filterThunk;
        private FilterToSQLBuilder sqlBuilder = null;
        private SearchOperator searchOperator = SearchOperator.And;

        public PagingCriteriaBinder()
        {
        }

        public PagingCriteriaBinder(Type filterCollectType, SearchOperator sOperator)
        {
            searchOperator = sOperator;

            if (filterCollectType != null)
            {
                if (!typeof(IFilterCollect).IsAssignableFrom(filterCollectType))
                {
                    throw new ArgumentException(string.Format("{0} doesn't implement IFilterCollect.", filterCollectType.FullName), "filterCollectType");
                }

                this.filterThunk = () => (IFilterCollect)Activator.CreateInstance(filterCollectType);
            }
        }

        internal FilterToSQLBuilder FilterToSQLBuilder
        {
            get
            {
                if (this.sqlBuilder == null)
                {
                    this.sqlBuilder = FilterToSQLBuilder.Current;
                }

                return this.sqlBuilder;
            }

            set
            {
                this.sqlBuilder = value;
            }
        }

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var queryString = controllerContext.HttpContext.Request.QueryString;

            PagingCriteria criteria = new PagingCriteria();
            this.FetchBasic(queryString, ref criteria);

            if (this.IsIncludeSearch(queryString))
            {
                this.FetchSearchFilters(queryString, ref criteria);
            }

            return criteria;
        }

        private void FetchBasic(NameValueCollection queryString, ref PagingCriteria criteria)
        {
            int pageIndex;
            if (!Int32.TryParse(queryString[PagingConst.PAGE], out pageIndex))
            {
                pageIndex = 1;
            }

            criteria.PageIndex = pageIndex;

            int pageSize;
            if (!Int32.TryParse(queryString[PagingConst.ROWS], out pageSize))
            {
                pageSize = 10;
            }

            criteria.PageSize = pageSize;

            if (queryString.AllKeys.Contains(PagingConst.SIDX))
            {
                criteria.Sidx = queryString[PagingConst.SIDX].ToString();
            }

            if (queryString.AllKeys.Contains(PagingConst.SORD))
            {
                criteria.Sord = queryString[PagingConst.SORD].ToString();
            }
        }

        private bool IsIncludeSearch(NameValueCollection queryString)
        {
            bool isSearch = false;
            bool.TryParse(queryString[PagingConst.SEARCH_FLAG], out isSearch);

            return isSearch;
        }

        private void FetchSearchFilters(NameValueCollection queryString, ref PagingCriteria criteria)
        {
            Dictionary<string, object> filters = new Dictionary<string, object>();

            IFilterCollect filterCollect = this.filterThunk();
            filters = filterCollect.Collect(queryString);

            if (filters.Count > 0)
            {
                List<object> values = new List<object>();
                criteria.SearchString = FilterToSQLBuilder.GenerateSQL(filters, Enum.GetName(typeof(SearchOperator), this.searchOperator).ToLower(), ref values);
                criteria.SearchValues = values.ToArray();
            }
        }
    }

    [AttributeUsage(ValidTargets, AllowMultiple = false, Inherited = false)]
    public sealed class PaingCriteriaBinderAttribute : CustomModelBinderAttribute
    {
        internal const AttributeTargets ValidTargets = AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Parameter;

        public PaingCriteriaBinderAttribute()
            : this(null, SearchOperator.And)
        {
        }

        public PaingCriteriaBinderAttribute(Type filterCollectType)
            : this(filterCollectType, SearchOperator.And)
        {
        }

        public PaingCriteriaBinderAttribute(Type filterCollectType, SearchOperator sOperator)
        {
            this.FilterCollectType = filterCollectType;
            this.Operator = sOperator;
        }

        public Type FilterCollectType
        {
            get;
            set;
        }

        public SearchOperator Operator
        {
            get;
            set;
        }

        public override IModelBinder GetBinder()
        {
            return new PagingCriteriaBinder(this.FilterCollectType, this.Operator);
        }
    }
}
